﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace iNet
{
	/// <summary>
	/// 提供字串相關功能。
	/// </summary>
	public static class StringUtility
	{
		#region Compare
		/*
		public static int Compare(IList<char> strA, int startA, int countA, IList<char> strB, int startB, int countB, bool ignoreCase)
		{
			//取得字串長度
			int strLengthA = (strA != null ? strA.Count : 0);
			int strLengthB = (strB != null ? strB.Count : 0);

			//確認參數
			if (startA < 0 || startA >= strLengthA)
				throw new ArgumentOutOfRangeException("startA");
			if (startB < 0 || startB >= strLengthB)
				throw new ArgumentOutOfRangeException("startB");
			if (countA < 0 || (startA + countA) > strLengthA)
				throw new ArgumentOutOfRangeException("countA");
			if (countB < 0 || (startB + countB) > strLengthB)
				throw new ArgumentOutOfRangeException("countB");
			
			//比較字串
			return CompareInternal(strA, startA, countA, strB, startB, countB, ignoreCase);
		}
		*/
		#endregion
		#region CompareInternal
		static int CompareInternal(IList<char> strA, int startA, int countA, IList<char> strB, int startB, int countB, bool ignoreCase)
		{
			//取得字串長度
			int endA = (startA + countA);
			int endB = (startB + countB);

			//比較字串內容
			if (!ignoreCase)
			{
				while (startA < endA && startB < endB)
				{
					char c1 = strA[startA++];
					char c2 = strB[startB++];
					if (c1 < c2)
						return -1;
					if (c1 > c2)
						return 1;
				}
			}
			else
			{
				while (startA < endA && startB < endB)
				{
					char c1 = char.ToLowerInvariant(strA[startA++]);
					char c2 = char.ToLowerInvariant(strB[startB++]);
					if (c1 < c2)
						return -1;
					if (c1 > c2)
						return 1;
				}
			}

			//比較字串長度
			if (countA < countB)
				return -1;
			if (countA > countB)
				return 1;
			return 0;
		}
		#endregion
		#region ConvertEncoding
		/// <summary>
		/// 將指定字串轉換成指定編碼可以表示的字串，無法表示的字元將以 ? 字元替代。
		/// </summary>
		/// <param name="value">指定的字串。</param>
		/// <param name="encoding">指定的編碼。</param>
		/// <returns>轉換後的字串。</returns>
		/// <exception cref="ArgumentNullException">encoding 為 Null (Visual Basic 為 Nothing)。</exception>
		public static string ConvertEncoding(this string value, Encoding encoding)
		{
			return ConvertEncoding(value, encoding, '?');
		}
		/// <summary>
		/// 將指定字串轉換成指定編碼可以表示的字串，無法表示的字元將以指定字元替代。
		/// </summary>
		/// <param name="value">指定的字串。</param>
		/// <param name="encoding">指定的編碼。</param>
		/// <param name="replaceChar">替代字元。</param>
		/// <returns>轉換後的字串。</returns>
		/// <exception cref="ArgumentNullException">encoding 為 Null (Visual Basic 為 Nothing)。</exception>
		public static string ConvertEncoding(this string value, Encoding encoding, char replaceChar)
		{
			//確認
			if (encoding == null)
				throw new ArgumentNullException("encoding");
			if (string.IsNullOrEmpty(value))
				return value;

			//轉換
			char[] chars = value.ToCharArray();
			bool replaced = false;
			for (int i = 0, length = chars.Length; i < length; i++)
			{
				if (chars[i] != '?')
				{
					if (encoding.GetChars(encoding.GetBytes(chars, i, 1))[0] == '?')
					{
						chars[i] = replaceChar;
						replaced = true;
					}
				}
			}

			//回傳
			return (replaced ? new string(chars) : value);
		}
		#endregion
		#region EndsWith
		/// <summary>
		/// 確認指定 StringBuilder 的結尾部分是否與指定的字串相符。
		/// </summary>
		/// <param name="str">指定的 StringBuilder。</param>
		/// <param name="value">與結尾比較的字串。</param>
		/// <returns>True 表示 StringBuilder 的結尾部分與指定的字串相符，False 表示不相符。</returns>
		/// <exception cref="ArgumentNullException">str 為 Null (Visual Basic 為 Nothing)。</exception>
		public static bool EndsWith(this StringBuilder str, string value)
		{
			return EndsWith(str, value, false);
		}
		/// <summary>
		/// 確認指定 StringBuilder 的結尾部分是否與指定的字串相符。
		/// </summary>
		/// <param name="str">指定的 StringBuilder。</param>
		/// <param name="value">與結尾比較的字串。</param>
		/// <param name="ignoreCase">是否要忽略字元的大小寫。</param>
		/// <returns>True 表示 StringBuilder 的結尾部分與指定的字串相符，False 表示不相符。</returns>
		/// <exception cref="ArgumentNullException">str 為 Null (Visual Basic 為 Nothing)。</exception>
		public static bool EndsWith(this StringBuilder str, string value, bool ignoreCase)
		{
			//確認參數
			if (str == null)
				throw new ArgumentNullException("str");
			if (string.IsNullOrEmpty(value))
				return true;

			//比較結尾字串
			int strLength = str.Length;
			int valueLength = value.Length;
			if (strLength >= valueLength)
			{
				if (!ignoreCase)
				{
					for (int strIndex = (strLength - 1), valueIndex = (valueLength - 1); valueIndex >= 0; --strIndex, --valueIndex)
					{
						if (str[strIndex] != value[valueIndex])
							return false;
					}
				}
				else
				{
					for (int strIndex = (strLength - 1), valueIndex = (valueLength - 1); valueIndex >= 0; --strIndex, --valueIndex)
					{
						if (char.ToLowerInvariant(str[strIndex]) != char.ToLowerInvariant(value[valueIndex]))
							return false;
					}
				}
				return true;
			}
			return false;
		}
		#endregion
		#region GetEncoding
		/// <summary>
		/// 判斷指定資料流中的文字編碼。
		/// </summary>
		/// <param name="stream">提供文字資料的資料流。</param>
		/// <param name="defaultEncoding">預設的編碼。</param>
		/// <returns>指定資料流中的文字編碼，若無法辨識則傳回 defaultEncoding。</returns>
		/// <exception cref="ArgumentNullException">stream 為 Null (Visual Basic 為 Nothing)。</exception>
		public static Encoding GetEncoding(Stream stream, Encoding defaultEncoding)
		{
			//確認
			if (stream == null)
				throw new ArgumentNullException("stream");

			//取得目前位置
			long initPosition;
			try
			{
				initPosition = stream.Position;
			}
			catch
			{
				return defaultEncoding;
			}

			//判斷編碼
			Encoding encoding = null;
			try
			{
				if (stream.Length >= 2)
				{
					//讀取前置位元組
					byte[] buffer4 = new byte[4];
					stream.Read(buffer4, 0, 2);

					//判斷編碼
					if (buffer4[0] == 0xFF && buffer4[1] == 0xFE)
						encoding = Encoding.Unicode;
					else if (buffer4[0] == 0xFE && buffer4[1] == 0xFF)
						encoding = Encoding.BigEndianUnicode;
					else if (stream.Length >= 3)
					{
						//讀取前置位元組
						stream.Position = 0;
						stream.Read(buffer4, 0, 3);

						//判斷編碼
						if (buffer4[0] == 0xEF && buffer4[1] == 0xBB && buffer4[2] == 0xBF)
							encoding = Encoding.UTF8;
						else if (stream.Length >= 4)
						{
							//讀取前置位元組
							stream.Position = 0;
							stream.Read(buffer4, 0, 4);

							//判斷編碼
							if (buffer4[0] == 0xFF && buffer4[1] == 0xFE && buffer4[2] == 0 && buffer4[3] == 0)
								encoding = Encoding.UTF32;
							else if (buffer4[0] == 0 && buffer4[1] == 0 && buffer4[2] == 0xFE && buffer4[3] == 0xFF)
								encoding = Encoding.GetEncoding(65006);
						}
					}
				}
			}
			catch
			{
				return defaultEncoding;
			}
			finally
			{
				//恢復資料流位置
				try
				{
					stream.Position = initPosition;
				}
				catch
				{ }
			}

			//回傳
			return (encoding == null ? defaultEncoding : encoding);
		}
		#endregion
		#region HasNonAnsiChar
		/// <summary>
		/// 確認指定字串中是否包含非 ANSI 編碼字元。
		/// </summary>
		/// <param name="str">指定的字串。</param>
		/// <returns>是否包含非 ANSI 編碼字元。</returns>
		public static bool HasNonAnsiChar(this string str)
		{
			if (str == null)
				return false;
			char[] chars = str.ToCharArray();
			Encoding encoding = Encoding.Default;
			for (int i = chars.Length - 1; i >= 0; --i)
			{
				if (chars[i] != '?')
				{
					if (encoding.GetChars(encoding.GetBytes(chars, i, 1))[0] == '?')
						return true;
				}
			}
			return false;
		}
		#endregion
		#region StartsWith
		/// <summary>
		/// 確認指定 StringBuilder 的開頭部分是否與指定的字串相符。
		/// </summary>
		/// <param name="str">指定的 StringBuilder。</param>
		/// <param name="value">與開頭比較的字串。</param>
		/// <returns>True 表示 StringBuilder 的開頭部分與指定的字串相符，False 表示不相符。</returns>
		/// <exception cref="ArgumentNullException">str 為 Null (Visual Basic 為 Nothing)。</exception>
		public static bool StartsWith(this StringBuilder str, string value)
		{
			return StartsWith(str, value, false);
		}
		/// <summary>
		/// 確認指定 StringBuilder 的開頭部分是否與指定的字串相符。
		/// </summary>
		/// <param name="str">指定的 StringBuilder。</param>
		/// <param name="value">與開頭比較的字串。</param>
		/// <param name="ignoreCase">是否要忽略字元的大小寫。</param>
		/// <returns>True 表示 StringBuilder 的開頭部分與指定的字串相符，False 表示不相符。</returns>
		/// <exception cref="ArgumentNullException">str 為 Null (Visual Basic 為 Nothing)。</exception>
		public static bool StartsWith(this StringBuilder str, string value, bool ignoreCase)
		{
			//確認參數
			if (str == null)
				throw new ArgumentNullException("str");
			if (string.IsNullOrEmpty(value))
				return true;

			//比較開頭字串
			int strLength = str.Length;
			int valueLength = value.Length;
			if (strLength >= valueLength)
			{
				if (!ignoreCase)
				{
					for (int strIndex = (valueLength - 1), valueIndex = (valueLength - 1); valueIndex >= 0; --strIndex, --valueIndex)
					{
						if (str[strIndex] != value[valueIndex])
							return false;
					}
				}
				else
				{
					for (int strIndex = (valueLength - 1), valueIndex = (valueLength - 1); valueIndex >= 0; --strIndex, --valueIndex)
					{
						if (char.ToLowerInvariant(str[strIndex]) != char.ToLowerInvariant(value[valueIndex]))
							return false;
					}
				}
				return true;
			}
			return false;
		}
		#endregion
	}
}